package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;

import model.IModel;
import model.Model.Status;
import controller.states.GameOverState;
import controller.states.InitialState;
import controller.states.RestartLevelState;
import controller.states.VictoryState;


/**
 * 
 * Controller class
 * 
 * Responsible for handling user input and state transitions as well as telling
 * the model to update on a timer.
 * 
 */


public class Controller implements IController {
	
	public enum StateType {
		INITIAL, GAMEPLAY, RESTART_LEVEL, GAMEOVER, VICTORY;
	}

	private IState gameState;
	StateType currentState;
	private IModel gameModel;
	private Timer timer;

	private static final int PERIOD = 33; // repeat every 33 Ms

	/**
	 * Controller
	 * 
	 * Constructor for the controller class, takes in an interface to a Model
	 * 
	 * @param Model
	 */
	public Controller(IModel m) {
		gameModel = m;

		timer = new Timer(PERIOD, new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				update();
			}
		});

		setState(new InitialState(this));
	}  

	/**
	 * checkDeath
	 * 
	 * the check death will check whether or not the playerCharacter has died or
	 * gotten a game over it will then change the gameStatus based upon this and
	 * then revive the PlayerCharacter
	 */
	private void checkDeath() {
		Status status = gameModel.getStatus();
		if (status == Status.DEAD) {
			setState(new RestartLevelState(this));
			gameModel.revivePlayer();
		} else if (status == Status.GAMEOVER) {
			setState(new GameOverState(this));
			gameModel.revivePlayer();
		}
	}

	/**
	 * checkVictory
	 * 
	 * checkVictory calls the checkVictory function inside of the model and
	 * updates the current gameState accordingly
	 */
	private void checkVictory() {
		if (gameModel.checkVictory()) {
			setState(new VictoryState(this));
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see controller.IController#evaluateKeyPressed(int)
	 */
	@Override
	public void evaluateKeyPressed(int keyCode) {
		gameState.handleKeyPressed(keyCode);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see controller.IController#evaluateKeyReleased(int)
	 */
	@Override
	public void evaluateKeyReleased(int keyCode) {
		gameState.handleKeyReleased(keyCode);
	}

	/**
	 * loadInitalLevel
	 * 
	 * tells the Model to load the first Level
	 */
	public void loadInitialLevel() {
		gameModel.loadLevel(1);
	}

	/**
	 * loadNextLevel
	 * 
	 * tells the model to load the next level
	 */
	public void loadNextLevel() {
		gameModel.loadNextLevel();
	}

	/**
	 * moveJump
	 * 
	 * tells the Model to jump
	 */
	public void moveJump() {
		gameModel.moveJump();
	}

	/**
	 * moveLeft
	 * 
	 * tells the model to move left
	 */
	public void moveLeft() {
		gameModel.moveLeft();
	}

	/**
	 * moveRight
	 * 
	 * tells the model to move right
	 */
	public void moveRight() {
		gameModel.moveRight();
	}

	/**
	 * moveStop
	 * 
	 * tells the model to stop
	 */
	public void moveStop() {
		gameModel.moveStop();
	}

	/**
	 * reloadLevel
	 * 
	 * if the user dies the game will reload the current level
	 */
	public void reloadLevel() {
		gameModel.loadLevel(gameModel.getCurrentLevel());
	}

	/**
	 * setLoadingLevel
	 * 
	 * loads the level with the interger number loadingLevel
	 * 
	 * @param loadingLevel
	 */
	public void setLoadingLevel(boolean loadingLevel) {
		gameModel.setLoadingLevel(loadingLevel);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see controller.IController#setState(controller.IState)
	 */
	@Override
	public void setState(IState newState) {
		gameState = newState;
		StateType current = gameState.getState();
		switch (current) {
		case INITIAL: {
			loadInitialLevel();
			break;
		}
		case GAMEPLAY: {
			timer.start();
			break;
		}
		case RESTART_LEVEL: {
			timer.stop();
			reloadLevel();
			break;
		}
		case GAMEOVER: {
			timer.stop();
			loadInitialLevel();
			break;
		}
		case VICTORY: {
			timer.stop();
			gameModel.setLoadingLevel(true);
			gameModel.update();
			if (gameModel.getCurrentLevel() != 1 || gameModel.checkVictory())
				loadNextLevel();
			break;
		}
		}
	}

	/**
	 * togglePause
	 * 
	 * pauses or unpauses the model
	 */
	public void togglePause() {
		gameModel.togglePause();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see controller.IController#update()
	 */
	@Override
	public void update() {
		checkVictory();
		gameModel.update();
		checkDeath();
	}
}
